#pragma once
// -------------------------------------------------
// ZED Kit
// -------------------------------------------------
//   File Name: utility.hpp
//      Author: Ziming Li
//     Created: 2021-05-21
// -------------------------------------------------
// Copyright (C) 2021 MingYang Software Technology.
// -------------------------------------------------

#ifndef ZED_UTILITY_HPP
#define ZED_UTILITY_HPP

#include <functional>
#include <vector>
#include "./string.hpp"

namespace zed {

/**
 * noncopyable
 */

class noncopyable
{
protected:
    noncopyable(void) = default;
private:
    noncopyable(const noncopyable&) = delete;
    noncopyable& operator=(const noncopyable&) = delete;
};

/**
 * scoped_finalizer
 */

class scoped_finalizer
{
public:
    scoped_finalizer(std::function<void()> &&finalizer) : m_finalizer(std::move(finalizer)) {}
    ~scoped_finalizer(void);

    void discard(void) { m_discarded = true; }
private:
    const std::function<void()> m_finalizer;
    bool m_discarded = false;
};

/**
 * scoped_swap
 */

template <typename T>
class scoped_swap
{
public:
    explicit scoped_swap(T &val);
    scoped_swap(T &dst, const T &new_val);
    ~scoped_swap(void);
private:
    T &m_dst;
    const T m_old_val;
};

/**
 * unique_id
 */

template <typename T, typename ID = int, typename NEXT = ID>
class unique_id
{
public:
    unique_id(void) : m_id(next()) { }
    operator ID() const { return m_id; }
private:
    static ID next(void);

    const ID m_id;
    static_assert(std::is_integral<ID>::value);
};

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Implementations

inline scoped_finalizer::~scoped_finalizer(void)
{
    if (!m_discarded)
        m_finalizer();
}

template <typename T>
scoped_swap<T>::scoped_swap(T &val) : m_dst(val), m_old_val(val)
{
}

template <typename T>
scoped_swap<T>::scoped_swap(T &dst, const T &new_val) : m_dst(dst), m_old_val(dst)
{
    m_dst = new_val;
}

template <typename T>
scoped_swap<T>::~scoped_swap(void)
{
    m_dst = m_old_val;
}

template <typename T, typename ID, typename NEXT>
ID unique_id<T, ID, NEXT>::next(void)
{
    static NEXT s_next(1);
    return s_next++;
}

} // namespace zed

#endif // ZED_UTILITY_HPP
